#pragma once

#include <string>
#include <iostream>
#include <assert.h>
#include "../sequence/common.h"
#include "../sequence/MatrixTests.h"
#include "../sequence/Matrix.h"
#include "../sequence/RecurrenceOperator.h"
#include "../sequence/MatrixGenerator.h"
#include "../sequence/ResultStorage.h"
#include "../sequence/AlgebraFactory.h"


namespace testMatrix
{
	template<int size>
	void simmetryTest(const Matrix<size> &a, const Matrix<size> &b)
	{
		assert((a == b) == (b == a));
	}

	template<int size>
	void reflectTest(const Matrix<size> &a)
	{
		assert(a == a);
	}

	template<int size>
	void copyTest(const Matrix<size> &m)
	{
		Matrix<size> _m(m);
		assert(m == _m);
	}

	template<int size>
	void multTest(
		const IAlgebra &algebra,
		const typename MatrixOperator<size>::ControlSequence &c,
		const typename MatrixOperator<size>::Factorset &f,
		const Matrix<size> &M,
		const Matrix<size> &result)
	{
		Matrix<size> _m(M);
		MatrixOperator<size, 2, left_operator_tag> rop(&algebra, c, f);
		rop.applyTo(&_m);
		assert(result == _m);

	}

	void test0()
	{
		Matrix<> a;
		a.set(0, 0, 1);
		a.set(0, 1, 234);
		a.set(1, 0, 453);
		a.set(1, 1, 98);
		Matrix<> b;
		b.set(0, 0, 1);
		b.set(0, 1, 234);
		b.set(1, 0, 453);
		b.set(1, 1, 98);
		assert(a == b);
		simmetryTest(a, b);
		copyTest(a);
		copyTest(b);
		reflectTest(a);
		reflectTest(b);
		b.set(0, 0, 11);
		assert(!(a == b));
		simmetryTest(a, b);

		Matrix<3> a3;
		a3.set(0, 0, 1);
		a3.set(0, 1, 234);
		a3.set(0, 2, 23);
		a3.set(1, 0, 53);
		a3.set(1, 1, 4);
		a3.set(1, 2, 7);
		a3.set(2, 0, 98);
		a3.set(2, 1, 5);
		a3.set(2, 2, 9);
		Matrix<3> b3;
		b3.set(0, 0, 1);
		b3.set(0, 1, 234);
		b3.set(0, 2, 23);
		b3.set(1, 0, 53);
		b3.set(1, 1, 4);
		b3.set(1, 2, 7);
		b3.set(2, 0, 98);
		b3.set(2, 1, 5);
		b3.set(2, 2, 9);
		assert(a3 == b3);
		simmetryTest(a3, b3);
		copyTest(a3);
		copyTest(b3);
		reflectTest(a3);
		reflectTest(b3);
		b3.set(0, 0, 11);
		assert(!(a3 == b3));
		simmetryTest(a3, b3);
	}

	void test1()
	{
		RingZ256 algebra;

		MatrixOperator<2, 2, left_operator_tag>::ControlSequence c;
		c.setElement(0, 2);
		c.setElement(1, 1);
		c.setElement(2, 0);

		Matrix<> M;
		M.set(0, 0, 1);
		M.set(0, 1, 2);
		M.set(1, 0, 3);
		M.set(1, 1, 4);
		MatrixOperator<2, 2, left_operator_tag>::Factorset f;
		f.setElement(0, algebra.unityElement());
		f.setElement(1, algebra.unityElement());
		Matrix<> R;
		R.set(0, 0, 3);
		R.set(0, 1, 4);
		R.set(1, 0, 4);
		R.set(1, 1, 6);
		multTest(algebra, c, f, M, R);

		c.setElement(0, 3);
		c.setElement(1, 1);
		c.setElement(2, 0);
		Matrix<3> a;
		for(int i = 0; i < 9; i++)
		{
			a.set(i / 3, i % 3, i + 1);
		}
		Matrix<3> R3;
		for(int i = 0; i < 6; i++)
		{
			R3.set(i / 3, i % 3, i + 4);
		}
		R3.set(2, 0, 5);
		R3.set(2, 1, 7);
		R3.set(2, 2, 9);
		multTest(algebra, c, f, a, R3);
	}

	void runtests()
	{
		test0();
		test1();
	}
}